package com.piece.core.web.interceptor;

import com.piece.core.framework.constant.ApplicationConstants;
import com.piece.core.framework.constant.ExceptionConstants;
import com.piece.core.framework.exception.PermissionException;
import com.piece.core.framework.support.dto.StreamBlockDTO;
import com.piece.core.framework.service.StreamBlockService;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.async.AsyncManager;
import com.piece.core.framework.util.cache.Cache;
import com.piece.core.framework.util.cache.CacheFactory;
import com.piece.core.framework.util.net.IpUtil;
import com.piece.core.framework.properties.ApplicationProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

/**
 * 频繁攻击网站黑名单拦截器
 */
@Slf4j
public class StreamDataBlockInterceptor extends InterceptorHookSupport {

    private final String PREFIX = "STREAM_DATA_LIMIT_";

    @Autowired
    private ApplicationProperties applicationProperties;

    @Autowired
    private CacheFactory cacheFactory;

    @Autowired(required = false)
    private StreamBlockService streamBlockService;

    @Override
    public String[] getInterceptor() {
        return null;
    }

    @Override
    public void preHandle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) {
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) throws Exception {
        boolean enable = Convert.toBool(this.applicationProperties.getValue(ApplicationConstants.SYSTEM_BLACK_ENABLE), false);
        if (enable && null != streamBlockService) {
            String ip = IpUtil.getIp(request);
            List<StreamBlockDTO> streams = streamBlockService.queryByIp(ip);
            if (null == streams || streams.size() == 0) {
                int limitTime = Convert.toInt(this.applicationProperties.getValue(ApplicationConstants.SYSTEM_BLACK_LIMIT_TIME), 5);
                int limitCount = Convert.toInt(this.applicationProperties.getValue(ApplicationConstants.SYSTEM_BLACK_LIMIT_COUNT), 8);
                urlHandle(request, ip, limitTime, limitCount);
            } else {
                throw new PermissionException(I18nUtil.message(ExceptionConstants.ACCESS));
            }
        }
    }

    private void urlHandle(HttpServletRequest request, String ip, int limitTime,int limitCount) throws Exception {
        try {
            String url = request.getRequestURL().toString();
            if (url.indexOf("receiveMessage") > 0 || url.indexOf("css") > 0
                    || url.indexOf("fonts") > 0
                    || url.indexOf("image") > 0
                    || url.indexOf("js") > 0
                    || url.indexOf("lib") > 0
                    || url.indexOf("static") > 0) {
                return;
            }
            String key = PREFIX.concat(url).concat(ip);
            Cache cache = cacheFactory.get();
            if (null == cache.get(key, Integer.class) || cache.get(key, Integer.class) == 0) {
                cache.set(key, 1);
            } else {
                cache.set(key, cache.get(key, Integer.class) + 1);
            }

            int count = cache.get(key, Integer.class);
            if (count > 0) {
                AsyncManager.getInstance().execute(new TimerTask() {
                    @Override
                    public void run() {
                        cache.delete(key);
                    }
                }, limitTime, TimeUnit.SECONDS);
            }

            if (count > limitCount) {
                int blockTime = Convert.toInt(this.applicationProperties.getValue(ApplicationConstants.SYSTEM_BLACK_BLOCK_TIME), 30);
                streamBlockService.save(ip);

                if (blockTime > 0) {
                    AsyncManager.getInstance().execute(new TimerTask() {
                        @Override
                        public void run() {
                            streamBlockService.delete(ip);
                        }
                    }, blockTime, TimeUnit.MINUTES);
                }
                throw new PermissionException(I18nUtil.message(ExceptionConstants.ACCESS));
            }
        } catch (Exception e) {
            throw new PermissionException(I18nUtil.message(ExceptionConstants.ACCESS));
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler, Exception ex) {
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) {
    }
}
